前因
最近遇到一个问题,涉及到一个三方应用在系统中的方向显示。三方应用设置了强制竖屏,我需要将其变成横屏。
应用设置屏幕方向有两种方式,一种是在Manifest文件里配置:
android:screenOrientation="portrait"
另一种是通过接口设置。
setRequestedOrientation(int orientation)
通过命令查看三方应用的AndroidManifest.xml文件:
aapt dump xmltree xxxx.apk AndroidManifest.xml
看到应用是在其AndroidManifest.xml文件中指定了应用的方向。
所以,要解决这个问题,就涉及到一个知识点:Andriod系统是如何解析AndroidManifest文件的?
或者说,AndroidManifest是怎么起作用的?
PMS启动后的部分工作
PMS启动之后,首先会设置各种要扫描的目录,然后通过scanDirLI
方法对一些目录比如system/app,data/app,vendor/app进行apk的扫描。
private void scanDirLI(File dir, final int parseFlags, int scanFlags, long currentTime) {
final File[] files = dir.listFiles();
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
try {
//扫描安装
scanPackageTracedLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
scanFlags, currentTime, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
//删除无效应用的用户数据
if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
removeCodePathLI(file);
}
}
}
}
其中调用了scanPackageTracedLI方法,其实就是跟踪package扫描:
/**
* Traces a package scan.
* @see #scanPackageLI(File, int, int, long, UserHandle)
*/
private PackageParser.Package scanPackageTracedLI(File scanFile, final int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
try {
return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
接着进入
/**
* Traces a package scan.
* @see #scanPackageLI(File, int, int, long, UserHandle)
*/
private PackageParser.Package scanPackageTracedLI(File scanFile, final int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
try {
return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
接着进入scanPackageLI。这个方法有两个实现:
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user);
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,
int scanFlags, long currentTime, UserHandle user);
两者唯一的区别是签名列表中第一个参数,第一种实现为File类型的对象,而第二种实现为PackageParser.Package类型的对象。在具体解析某个文件时会先调用第一种实现解析apk文件,再调用第二种实现将解析后的信息保存至PMS:
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
//创建PackageParser
PackageParser pp = new PackageParser();
final PackageParser.Package pkg;
try {
//调用PackageParser的parsePackage方法解析APK文件
pkg = pp.parsePackage(scanFile, parseFlags);
} catch (PackageParserException e) {}
//调用该方法的第二种实现,将解析后的信息保存至PMS
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user);
return scannedPkg;
}
这里就调用到了PackageParser类的parsePackage方法进行对APK的解析。
PackageParser类
PackageParser类主要负责对应用AndroidManifest.xml文件的解析工作,所以第一个常亮定义就是:
private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
解析AndroidManifest文件需要对不同xml节点分别解析,所以PackageParser部分的结构是如下形式:
public class PackageParser {
public static class Component {}
public final static class Activity extends Component {}
public final static class Instrumentation extends Component {}
public final static class Permission extends Component {}
public final static class PermissionGroup extends Component {}
public final static class Provider extends Component {}
public final static class Service extends Component {}
}
可以看到,PackageParser为Activity、Service、Provider、Permission等组件在其内部以内部类的方式创建了对应的类,按照解释器模式的定义,这些类其实都对应AndroidManifest.xml文件中的一个标签,其在对该配置文件解析时充分运用了解释器模式分离实现、解释执行的特性。
好了,前面代码流程PMS走到了parsePackage的地方,这个方法是PackageParser最关键的方法,它的实现如下:
/**
* Parse the package at the given location. Automatically detects if the
* package is a monolithic style (single APK file) or cluster style
* (directory of APKs).
*
* This performs sanity checking on cluster style packages, such as
* requiring identical package name and version codes, a single base APK,
* and unique split names.
*
* Note that this does not perform signature verification; that
* must be done separately in {@link #collectCertificates(Package, int)}.
*
* @see #parsePackageLite(File, int)
*/
public Package parsePackage(File packageFile, int flags) throws PackageParserException {
if (packageFile.isDirectory()) {
return parseClusterPackage(packageFile, flags);
} else {
return parseMonolithicPackage(packageFile, flags);//该方法已弃用
}
}
下面是parseClusterPackage方法:
/**
* Parse all APKs contained in the given directory, treating them as a
* single package. This also performs sanity checking, such as requiring
* identical package name and version codes, a single base APK, and unique
* split names.
*
* Note that this does not perform signature verification; that
* must be done separately in {@link #collectCertificates(Package, int)}.
*/
private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
final PackageLite lite = parseClusterPackageLite(packageDir, 0);
//...
final AssetManager assets = new AssetManager();
try {
// Load the base and all splits into the AssetManager
// so that resources can be overriden when parsing the manifests.
loadApkIntoAssetManager(assets, lite.baseCodePath, flags);
//...
final File baseApk = new File(lite.baseCodePath);
final Package pkg = parseBaseApk(baseApk, assets, flags);
//...
if (!ArrayUtils.isEmpty(lite.splitNames)) {
final int num = lite.splitNames.length;
pkg.splitNames = lite.splitNames;
pkg.splitCodePaths = lite.splitCodePaths;
pkg.splitRevisionCodes = lite.splitRevisionCodes;
pkg.splitFlags = new int[num];
pkg.splitPrivateFlags = new int[num];
for (int i = 0; i < num; i++) {
parseSplitApk(pkg, i, assets, flags);
}
}
pkg.setCodePath(packageDir.getAbsolutePath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
} finally {
IoUtils.closeQuietly(assets);
}
}
后面是很长的调用路线:
parsePackage->parseClusterPackage->parseBaseApk->parseBaseApkCommon->parseBaseApkChild->parseBaseApplication->parseSplitApk
其中最关键的一个解析方法是parseBaseApkCommon方法,这里就对每一个Tag分别调用对应的parse方法。
/**
* This is the common parsing routing for handling parent and child
* packages in a base APK. The difference between parent and child
* parsing is that some tags are not supported by child packages as
* well as some manifest attributes are ignored. The implementation
* assumes the calling code has already handled the manifest tag if needed
* (this applies to the parent only).
*
* @param pkg The package which to populate
* @param acceptedTags Which tags to handle, null to handle all
* @param res Resources against which to resolve values
* @param parser Parser of the manifest
* @param flags Flags about how to parse
* @param outError Human readable error if parsing fails
* @return The package if parsing succeeded or null.
*
* @throws XmlPullParserException
* @throws IOException
*/
private Package parseBaseApkCommon(Package pkg, Set acceptedTags, Resources res,
XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
IOException {
mParseInstrumentationArgs = null;
mParseActivityArgs = null;
mParseServiceArgs = null;
mParseProviderArgs = null;
int type;
boolean foundApp = false;
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifest);
String str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
if (str != null && str.length() > 0) {
String nameError = validateName(str, true, false);
//...
pkg.mSharedUserId = str.intern();
pkg.mSharedUserLabel = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
}
pkg.installLocation = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_installLocation,
PARSE_DEFAULT_INSTALL_LOCATION);
pkg.applicationInfo.installLocation = pkg.installLocation;
//...
// Resource boolean are -1, so 1 means we don't know the value.
int supportsSmallScreens = 1;
int supportsNormalScreens = 1;
int supportsLargeScreens = 1;
int supportsXLargeScreens = 1;
int resizeable = 1;
int anyDensity = 1;
int outerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (acceptedTags != null && !acceptedTags.contains(tagName)) {
Slog.w(TAG, "Skipping unsupported element under : "
+ tagName + " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
if (tagName.equals(TAG_APPLICATION)) {
//...
if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
return null;
}
} else if (tagName.equals(TAG_OVERLAY)) {
//...
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals(TAG_KEY_SETS)) {
if (!parseKeySets(pkg, res, parser, outError)) {
return null;
}
} else if (tagName.equals(TAG_PERMISSION_GROUP)) {
if (parsePermissionGroup(pkg, flags, res, parser, outError) == null) {
return null;
}
} else if (tagName.equals(TAG_PERMISSION)) {
if (parsePermission(pkg, res, parser, outError) == null) {
return null;
}
} else if (tagName.equals(TAG_PERMISSION_TREE)) {
if (parsePermissionTree(pkg, res, parser, outError) == null) {
return null;
}
} else if (tagName.equals(TAG_USES_PERMISSION)) {
if (!parseUsesPermission(pkg, res, parser)) {
return null;
}
} else if (tagName.equals(TAG_USES_PERMISSION_SDK_M)
|| tagName.equals(TAG_USES_PERMISSION_SDK_23)) {
if (!parseUsesPermission(pkg, res, parser)) {
return null;
}
} else if (tagName.equals(TAG_USES_CONFIGURATION)) {
//...
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals(TAG_USES_FEATURE)) {
FeatureInfo fi = parseUsesFeature(res, parser);
pkg.reqFeatures = ArrayUtils.add(pkg.reqFeatures, fi);
//...
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals(TAG_FEATURE_GROUP)) {
FeatureGroupInfo group = new FeatureGroupInfo();
ArrayList features = null;
final int innerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
final String innerTagName = parser.getName();
if (innerTagName.equals("uses-feature")) {
FeatureInfo featureInfo = parseUsesFeature(res, parser);
//...
} else {
Slog.w(TAG, "Unknown element under : " + innerTagName +
" at " + mArchiveSourcePath + " " +
parser.getPositionDescription());
}
XmlUtils.skipCurrentTag(parser);
}
//...
} else if (tagName.equals(TAG_USES_SDK)) {
//...
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals(TAG_SUPPORT_SCREENS)) {
//...
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals(TAG_PROTECTED_BROADCAST)) {
//...
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals(TAG_INSTRUMENTATION)) {
if (parseInstrumentation(pkg, res, parser, outError) == null) {
return null;
}
} else if (tagName.equals(TAG_ORIGINAL_PACKAGE)) {
//...
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals(TAG_ADOPT_PERMISSIONS)) {
//...
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals(TAG_USES_GL_TEXTURE)) {
// Just skip this tag
XmlUtils.skipCurrentTag(parser);
continue;
} else if (tagName.equals(TAG_COMPATIBLE_SCREENS)) {
// Just skip this tag
XmlUtils.skipCurrentTag(parser);
continue;
} else if (tagName.equals(TAG_SUPPORTS_INPUT)) {//
XmlUtils.skipCurrentTag(parser);
continue;
} else if (tagName.equals(TAG_EAT_COMMENT)) {
// Just skip this tag
XmlUtils.skipCurrentTag(parser);
continue;
} else if (tagName.equals(TAG_PACKAGE)) {
if (!MULTI_PACKAGE_APK_ENABLED) {
XmlUtils.skipCurrentTag(parser);
continue;
}
if (!parseBaseApkChild(pkg, res, parser, flags, outError)) {
// If parsing a child failed the error is already set
return null;
}
} else if (tagName.equals(TAG_RESTRICT_UPDATE)) {
//...
XmlUtils.skipCurrentTag(parser);
} else if (RIGID_PARSER) {
outError[0] = "Bad element under : "
+ parser.getName();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
} else {
Slog.w(TAG, "Unknown element under : " + parser.getName()
+ " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
}
//...
return pkg;
}
可以看到,parsePackage方法的主要作用其实就是对AndroidManifest.xml配置文件中manifest下的每个子节点进行解析,这里主要来看看parseActivity方法是如何对activity节点进行解析的:
private Activity parseActivity(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError,
boolean receiver, boolean hardwareAccelerated)
throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity);
if (mParseActivityArgs == null) {
mParseActivityArgs = new ParseComponentArgs(owner, outError,
R.styleable.AndroidManifestActivity_name,
R.styleable.AndroidManifestActivity_label,
R.styleable.AndroidManifestActivity_icon,
R.styleable.AndroidManifestActivity_roundIcon,
R.styleable.AndroidManifestActivity_logo,
R.styleable.AndroidManifestActivity_banner,
mSeparateProcesses,
R.styleable.AndroidManifestActivity_process,
R.styleable.AndroidManifestActivity_description,
R.styleable.AndroidManifestActivity_enabled);
}
mParseActivityArgs.tag = receiver ? "" : "";
mParseActivityArgs.sa = sa;
mParseActivityArgs.flags = flags;
Activity a = new Activity(mParseActivityArgs, new ActivityInfo());
if (outError[0] != null) {
sa.recycle();
return null;
}
boolean setExported = sa.hasValue(R.styleable.AndroidManifestActivity_exported);
if (setExported) {
a.info.exported = sa.getBoolean(R.styleable.AndroidManifestActivity_exported, false);
}
a.info.theme = sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0);
a.info.uiOptions = sa.getInt(R.styleable.AndroidManifestActivity_uiOptions,
a.info.applicationInfo.uiOptions);
String parentName = sa.getNonConfigurationString(
R.styleable.AndroidManifestActivity_parentActivityName,
Configuration.NATIVE_CONFIG_VERSION);
if (parentName != null) {
String parentClassName = buildClassName(a.info.packageName, parentName, outError);
if (outError[0] == null) {
a.info.parentActivityName = parentClassName;
} else {
Log.e(TAG, "Activity " + a.info.name + " specified invalid parentActivityName " +
parentName);
outError[0] = null;
}
}
String str;
str = sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_permission, 0);
if (str == null) {
a.info.permission = owner.applicationInfo.permission;
} else {
a.info.permission = str.length() > 0 ? str.toString().intern() : null;
}
str = sa.getNonConfigurationString(
R.styleable.AndroidManifestActivity_taskAffinity,
Configuration.NATIVE_CONFIG_VERSION);
a.info.taskAffinity = buildTaskAffinityName(owner.applicationInfo.packageName,
owner.applicationInfo.taskAffinity, str, outError);
a.info.flags = 0;
if (sa.getBoolean(
R.styleable.AndroidManifestActivity_multiprocess, false)) {
a.info.flags |= ActivityInfo.FLAG_MULTIPROCESS;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_finishOnTaskLaunch, false)) {
a.info.flags |= ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_clearTaskOnLaunch, false)) {
a.info.flags |= ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_noHistory, false)) {
a.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysRetainTaskState, false)) {
a.info.flags |= ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_stateNotNeeded, false)) {
a.info.flags |= ActivityInfo.FLAG_STATE_NOT_NEEDED;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_excludeFromRecents, false)) {
a.info.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_allowTaskReparenting,
(owner.applicationInfo.flags&ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING) != 0)) {
a.info.flags |= ActivityInfo.FLAG_ALLOW_TASK_REPARENTING;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs, false)) {
a.info.flags |= ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_showOnLockScreen, false)
|| sa.getBoolean(R.styleable.AndroidManifestActivity_showForAllUsers, false)) {
a.info.flags |= ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_immersive, false)) {
a.info.flags |= ActivityInfo.FLAG_IMMERSIVE;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_systemUserOnly, false)) {
a.info.flags |= ActivityInfo.FLAG_SYSTEM_USER_ONLY;
}
if (!receiver) {
if (sa.getBoolean(R.styleable.AndroidManifestActivity_hardwareAccelerated,
hardwareAccelerated)) {
a.info.flags |= ActivityInfo.FLAG_HARDWARE_ACCELERATED;
}
a.info.launchMode = sa.getInt(
R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE);
a.info.documentLaunchMode = sa.getInt(
R.styleable.AndroidManifestActivity_documentLaunchMode,
ActivityInfo.DOCUMENT_LAUNCH_NONE);
a.info.maxRecents = sa.getInt(
R.styleable.AndroidManifestActivity_maxRecents,
ActivityManager.getDefaultAppRecentsLimitStatic());
a.info.configChanges = sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0);
a.info.softInputMode = sa.getInt(
R.styleable.AndroidManifestActivity_windowSoftInputMode, 0);
a.info.persistableMode = sa.getInteger(
R.styleable.AndroidManifestActivity_persistableMode,
ActivityInfo.PERSIST_ROOT_ONLY);
if (sa.getBoolean(R.styleable.AndroidManifestActivity_allowEmbedded, false)) {
a.info.flags |= ActivityInfo.FLAG_ALLOW_EMBEDDED;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_autoRemoveFromRecents, false)) {
a.info.flags |= ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_relinquishTaskIdentity, false)) {
a.info.flags |= ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_resumeWhilePausing, false)) {
a.info.flags |= ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
}
a.info.screenOrientation = sa.getInt(
R.styleable.AndroidManifestActivity_screenOrientation,
SCREEN_ORIENTATION_UNSPECIFIED);
a.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
final boolean appDefault = (owner.applicationInfo.privateFlags
& PRIVATE_FLAG_RESIZEABLE_ACTIVITIES) != 0;
// This flag is used to workaround the issue with ignored resizeableActivity param when
// either targetSdkVersion is not set at all or tag is below
// tag in AndroidManifest. If this param was explicitly set to 'false' we need to set
// corresponding resizeMode regardless of targetSdkVersion value at this point in time.
final boolean resizeableSetExplicitly
= sa.hasValue(R.styleable.AndroidManifestActivity_resizeableActivity);
final boolean resizeable = sa.getBoolean(
R.styleable.AndroidManifestActivity_resizeableActivity, appDefault);
if (resizeable) {
if (sa.getBoolean(R.styleable.AndroidManifestActivity_supportsPictureInPicture,
false)) {
a.info.resizeMode = RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
} else {
a.info.resizeMode = RESIZE_MODE_RESIZEABLE;
}
} else if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N
|| resizeableSetExplicitly) {
a.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
} else if (!a.info.isFixedOrientation() && (a.info.flags & FLAG_IMMERSIVE) == 0) {
a.info.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysFocusable, false)) {
a.info.flags |= FLAG_ALWAYS_FOCUSABLE;
}
a.info.lockTaskLaunchMode =
sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
a.info.encryptionAware = a.info.directBootAware = sa.getBoolean(
R.styleable.AndroidManifestActivity_directBootAware,
false);
a.info.requestedVrComponent =
sa.getString(R.styleable.AndroidManifestActivity_enableVrMode);
} else {
a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
a.info.configChanges = 0;
if (sa.getBoolean(R.styleable.AndroidManifestActivity_singleUser, false)) {
a.info.flags |= ActivityInfo.FLAG_SINGLE_USER;
if (a.info.exported && (flags & PARSE_IS_PRIVILEGED) == 0) {
Slog.w(TAG, "Activity exported request ignored due to singleUser: "
+ a.className + " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
a.info.exported = false;
setExported = true;
}
}
a.info.encryptionAware = a.info.directBootAware = sa.getBoolean(
R.styleable.AndroidManifestActivity_directBootAware,
false);
}
if (a.info.directBootAware) {
owner.applicationInfo.privateFlags |=
ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
}
sa.recycle();
if (receiver && (owner.applicationInfo.privateFlags
&ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
// A heavy-weight application can not have receives in its main process
// We can do direct compare because we intern all strings.
if (a.info.processName == owner.packageName) {
outError[0] = "Heavy-weight applications can not have receivers in main process";
}
}
if (outError[0] != null) {
return null;
}
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
if (parser.getName().equals("intent-filter")) {
ActivityIntentInfo intent = new ActivityIntentInfo(a);
if (!parseIntent(res, parser, true, true, intent, outError)) {
return null;
}
if (intent.countActions() == 0) {
Slog.w(TAG, "No actions in intent filter at "
+ mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
a.intents.add(intent);
}
} else if (!receiver && parser.getName().equals("preferred")) {
ActivityIntentInfo intent = new ActivityIntentInfo(a);
if (!parseIntent(res, parser, false, false, intent, outError)) {
return null;
}
if (intent.countActions() == 0) {
Slog.w(TAG, "No actions in preferred at "
+ mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
if (owner.preferredActivityFilters == null) {
owner.preferredActivityFilters = new ArrayList();
}
owner.preferredActivityFilters.add(intent);
}
} else if (parser.getName().equals("meta-data")) {
if ((a.metaData = parseMetaData(res, parser, a.metaData,
outError)) == null) {
return null;
}
} else if (!receiver && parser.getName().equals("layout")) {
parseLayout(res, parser, a);
} else {
if (!RIGID_PARSER) {
Slog.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
if (receiver) {
Slog.w(TAG, "Unknown element under : " + parser.getName()
+ " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
Slog.w(TAG, "Unknown element under : " + parser.getName()
+ " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
}
XmlUtils.skipCurrentTag(parser);
continue;
} else {
if (receiver) {
outError[0] = "Bad element under : " + parser.getName();
} else {
outError[0] = "Bad element under : " + parser.getName();
}
return null;
}
}
}
if (!setExported) {
a.info.exported = a.intents.size() > 0;
}
return a;
}
小结
综上可以了解到,Android应用的AndroidManifest.xml文件是在PMS启动时,扫描Apk的过程中通过PackageParser对Apk进行解析处理。
最后,回到开始的问题,我们需要处理的是对应应用的方向,所以在解析Activity的时候,解析到a.info.screenOrientation
的时候,根据应用的packageName进行对screenOrientation赋值即可。